package cn.wen233.srpc.core.service;

import cn.wen233.client.config.SunglowServerProperties;
import cn.wen233.srpc.basic.exception.BaseException;
import cn.wen233.srpc.basic.execute.Executor;
import cn.wen233.srpc.basic.loadbalance.LoadBalance;
import cn.wen233.srpc.basic.model.*;
import cn.wen233.srpc.basic.rollback.RollbackFactory;
import cn.wen233.srpc.basic.utils.IdUtils;
import cn.wen233.srpc.basic.utils.StringUtils;
import cn.wen233.srpc.core.execute.ClientExecutor;
import cn.wen233.srpc.core.loadbalance.RoundLoadBalance;
import cn.wen233.srpc.core.service.spi.SPIContext;
import cn.wen233.srpc.core.sunglow.http.Sunglow;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * Sunglow Reference InvocationHandler
 *
 * @author wenei
 * @date 2021-10-27 19:49
 */
public class SunglowReferenceInvocationHandler implements InvocationHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(SunglowReferenceInvocationHandler.class);

    private static Executor clientExecutor = SPIContext.loadSingleService(ClientExecutor.class);

    private static String TO_STRING = "toString";

    private static String HASH_CODE = "hashCode";

    private Class<?> fallbackFactory;

    private ServerConfig serverConfig;

    private LoadBalance loadBalance;

    public SunglowReferenceInvocationHandler() {
    }

    public SunglowReferenceInvocationHandler(ServerConfig config, Class<?> fallbackFactory) {
        this.serverConfig = config;
        this.fallbackFactory = fallbackFactory;
        this.loadBalance = SPIContext.loadSingleService(LoadBalance.class);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (TO_STRING.equals(method.getName())) {
            return proxy.getClass().getInterfaces()[0].getName() + "$Proxy";
        }
        if (HASH_CODE.equals(method.getName())) {
            return Objects.hashCode(proxy);
        }
        Request request = new Request();
        if (serverConfig != null) {
            ServerInstance serverInstance = loadBalance.select(serverConfig.getInstances());
            request.setHost(serverInstance.getHost());
            request.setPort(serverInstance.getPort());
        }
        // 获取代理类的接口的名称
        Class<?> interfaceClass = proxy.getClass().getInterfaces()[0];
        if (request.getHost() == null || request.getPort() == null) {
            throw new BaseException("{%s}代理对象没有配置Host或Port", interfaceClass.getName());
        }
        request.setRequestId(String.valueOf(IdUtils.snowId()));
        request.setServiceName(StringUtils.largeHumpConvertSmallHump(interfaceClass.getSimpleName()));
        request.setMethodName(method.getName());
        request.setParameters(args);
        request.setParameterTypes(method.getParameterTypes());
        Response response = null;
        try {
            response = clientExecutor.execute(request);
            // 添加Service访问记录
            Sunglow.insertServiceRecord(
                    SunglowServerProperties.getNamespaceId(),
                    SunglowServerProperties.getGroup(),
                    request.getServiceName()
            );
            // 添加服务实例访问记录
            Sunglow.insertInstanceRecord(
                    SunglowServerProperties.getNamespaceId(),
                    SunglowServerProperties.getGroup(),
                    request.getServiceName(),
                    request.getHost(),
                    request.getPort()
            );
        } catch (BaseException e) {
            if (fallbackFactory != void.class && fallbackFactory != Void.class && fallbackFactory != null) {
                // 判断fallbackFactory是否是RollbackFactory接口的实现类
                if (!Arrays.asList(fallbackFactory.getInterfaces()).contains(RollbackFactory.class)) {
                    throw new BaseException("{%s}设置的RollbackFactory错误", interfaceClass.getName());
                }
                // 获取熔断处理类实例
                RollbackFactory instance = (RollbackFactory) fallbackFactory.newInstance();
                Object rollbackService = instance.create(e);
                Method rollbackMethod = interfaceClass.getMethod(method.getName(), method.getParameterTypes());
                response = new Response();
                response.status(Status.ROLLBAKC);
                response.setData(rollbackMethod.invoke(rollbackService, args));
            }
        }
        if (response == null) {
            return null;
        }
        if (response.getCode() != 200) {
            LOGGER.warn("服务调用失败 返回信息：" + response.getMessage());
        }
        return response.getData();
    }
}
